﻿using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

namespace Nirvana
{
    public abstract class AbstractSceneGrid : MonoBehaviour
    {
        /// <summary>
        /// 每个cell块大小
        /// </summary>
        private static readonly int cellwidth = 64;
        private static readonly int cellheight = 64;

        /// <summary>
        /// Cell数量
        /// </summary>
        [Tooltip("The row count in this grid.")]
        [SerializeField]
        private int row;

        [SerializeField]
        [Tooltip("The column count in this grid.")]
        private int column;

        [SerializeField]
        [Tooltip("The size of each cell.")]
        private Vector2 cellSize = new Vector2(0.5f, 0.5f);

        [SerializeField]
        [Tooltip("The row count in this grid.")]
        private float baseHeight;

        [SerializeField]
        [Tooltip("The ground physics layer mask.")]
        [LayerMask]
        private int groundMask = 256;

        [Tooltip("Whether this scene grid is for editing.")]
        [SerializeField]
        private bool editing = true;

        [Tooltip("Whether this scene grid is Hide.")]
        [SerializeField]
        private bool isHide = true;

        public int Row { get { return this.row; } }
        public int Column { get { return this.column; } }

        public Vector2 CellSize { get { return this.cellSize; } set { this.cellSize = value; } }

        public  virtual int MaxRow { get { return 640; } }
        public virtual int MaxColumn { get { return 640; } }

        /// <summary>
        /// 多少个cell mesh
        /// </summary>
        private Mesh[] gridCellMeshs;
        /// <summary>
        /// 多少个cell Vertex
        /// </summary>
        private Vector3[][] gridCellVertexs;
        /// <summary>
        /// 每个cell颜色
        /// </summary>
        private Color32[][] gridCellColors;

        private Mesh[] gridOutlineMeshs;
        private Vector3[][] gridOutlineVertexs;

        private Transform root;
        public GameObject GetGridRoot() { return this.root ? this.root.gameObject : null; }

        public abstract AbstractSceneCell[] Cells { get; set; }
        public abstract AbstractSceneCell CreateCell();

        public void Initialize()
        {
            if(this.Cells == null || this.Cells.Length != this.row * this.column)
            {
                if (this.row == 0) this.row = 32;
                if (this.column == 0) this.column = 32;
                this.ResetCell(this.row, this.column);
            }

            if(this.root == null)
            {
                this.root = transform.Find("SceneGridRoot");
                if (this.root != null) this.root.hideFlags = isHide?HideFlags.HideAndDontSave: HideFlags.DontSave;
                else
                {
                    this.root = new GameObject("SceneGridRoot") { hideFlags = isHide ? HideFlags.HideAndDontSave : HideFlags.DontSave }.transform;
                    this.root.SetParent(transform, false);
                }


            }
            this.root.localPosition = Vector3.zero;
            this.root.localRotation = Quaternion.identity;
            this.root.localScale = Vector3.one;
            if(this.gridCellMeshs == null || this.gridCellVertexs == null || this.gridCellColors == null
                || this.gridOutlineMeshs == null || this.gridOutlineVertexs == null)
            UpdateMesh();
            this.SetGridVisible(this.editing);
        }

        public void SetGridVisible(bool visible)
        {
           // if(this.root!=null) this.root.gameObject.SetActive(visible);
        }

        public void SetGridHide(bool visible)
        {
            if (this.root != null) this.root.hideFlags = visible ? HideFlags.HideAndDontSave : HideFlags.DontSave;
        }

        public void Resize(int w,int h)
        {
            if (this.Cells == null) this.InitCell(w, h);
            else this.ResetCell(w, h);
            this.UpdateMesh();
        }
      
        public  void Resize(int x0, int y0, int x1, int y1)
        {
            this.InitCell(x0, y0, x1, y1);
            this.UpdateMesh();
        }

        private void InitCell(int w,int h)
        {
            this.row = w;
            this.column = h;
            if((this.Cells == null || this.Cells.Length == 0) && this.row > 0 && this.column > 0)
            {
                AbstractSceneCell[] cells = new AbstractSceneCell[w * h];
                for (int i = 0; i < w; i++)
                {
                    for (int j = 0; j < h; j++)
                    {
                        cells[i * h + j] = this.CreateCell();
                    }
                }
                this.Cells = cells;
            }
        }

        private float GetHeight(float x,float y)
        {
            Vector3 pos = new Vector3(x, 10000f, y);
            RaycastHit[] rays = Physics.RaycastAll(pos, Vector3.down, 100000f, this.groundMask);
            if (rays.Length == 0) return this.baseHeight;
            bool flag = false;
            float minY = float.NegativeInfinity;
            foreach(var hit in rays)
            {
                if(this.transform != hit.transform.parent)
                {
                    if(minY < hit.point.y)
                    {
                        minY = hit.point.y;
                        flag = true;
                    }
                }
            }

            if (flag)
            {
                return minY;
            }
            return this.baseHeight;

        }

        private void ResetCell(int w,int h)
        {
            AbstractSceneCell[] cells = new AbstractSceneCell[w * h];
            if(this.Cells != null)
            {
                for(int i = 0; i < w; i++)
                {
                    for(int j = 0; j < h; j++)
                    {
                        if (i < this.row && j < this.column)
                        {
                            int index = i * this.column + j;
                            if (index < this.Cells.Length) cells[i * h + j] = this.Cells[index];
                        }
                        else
                            cells[i * h + j] = this.CreateCell();
                    }
                }
            }
            else
            {
                for (int i = 0; i < w; i++)
                {
                    for (int j = 0; j < h; j++)
                    {
                         cells[i * h + j] = this.CreateCell();
                    }
                }
            }

            this.Cells = cells;
            this.row = w;
            this.column = h;
        }

        private void InitCell(int x0,int y0,int x1,int y1)
        {
            if(x0 != 0 || y0 != 0)
            {
                List<Transform> _list = new List<Transform>();
                var mTrans = transform;
                foreach (var t in mTrans)
                {
                    var tt = (Transform)t;
                    if (tt != this.root) _list.Add(tt);
                }

                foreach (var t in _list) t.SetParent(null, true);
                var pos = mTrans.position;
                pos.x -= x0 * this.cellSize.x;
                pos.z -= y0 * this.cellSize.y;
                mTrans.position = pos;

                foreach (var t in _list) t.SetParent(mTrans, true);
            }

            int w = this.row + x0 + x1;
            int h = this.column + y0 + y1;
            AbstractSceneCell[] cells = new AbstractSceneCell[w * h];
            if (this.Cells != null)
            {
                for (int i = 0; i < w; i++)
                {
                    for (int j = 0; j < h; j++)
                    {
                        if (i>=x0&& i <x0+ this.row && j>= y0&& j < y0+this.column)
                        {
                            int index =( i-x0) * this.column + j-y0;
                            if (index < this.Cells.Length) cells[i * h + j] = this.Cells[index];
                        }
                        else
                            cells[i * h + j] = this.CreateCell();
                    }
                }
            }
            else
            {
                for (int i = 0; i < w; i++)
                {
                    for (int j = 0; j < h; j++)
                    {
                        cells[i * h + j] = this.CreateCell();
                    }
                }
            }

            this.Cells = cells;
            this.row = w;
            this.column = h;

        }

        public void SetCellColor(int x,int y, Color32 color)
        {
            if (this.gridCellMeshs == null || this.gridCellVertexs == null || this.gridCellColors == null
                || this.gridOutlineMeshs == null || this.gridOutlineVertexs == null)
                UpdateMesh();
            //块数
            int minY = Mathf.CeilToInt(this.column * 1.0f / cellheight);
      
            int curX = Mathf.FloorToInt(x * 1.0f / cellwidth);
            int curY = Mathf.FloorToInt(y * 1.0f / cellheight);
            //起始块
            int startY = cellheight * curY;
            int h = Mathf.Min(startY + cellheight, this.column);
            //剩余块的尺寸
            int curH = h - startY;
            var colors = this.gridCellColors[curX * minY +curY];
            Mesh mesh = this.gridCellMeshs[curX * minY + curY];
            int num7 = x - cellwidth * curX;
            int num8 = y - cellheight * curY;
            colors[4 * (num7 * curH + num8)] = color;
            colors[4 * (num7 * curH + num8) + 1] = color;
            colors[4 * (num7 * curH + num8) + 2] = color;
            colors[4 * (num7 * curH + num8) + 3] = color;
            mesh.colors32 = colors;

        }

        public void UpdateMesh()
        {
            Transform[] childs = new Transform[this.root.childCount];
            for (int i = 0; i < this.root.childCount; i++)
                childs[i] = this.root.GetChild(i);

            foreach (var g in childs) DestroyImmediate(g.gameObject);
            //块数
            int cellX = Mathf.CeilToInt(this.row * 1.0f / cellwidth);
            int cellY = Mathf.CeilToInt(this.column * 1.0f / cellheight);
            gridCellMeshs = new Mesh[cellX * cellY];
            gridCellVertexs = new Vector3[cellX * cellY][];
            gridCellColors = new Color32[cellX * cellY][];

            gridOutlineMeshs = new Mesh[cellX * cellY];
            gridOutlineVertexs = new Vector3[cellX * cellY][];

            for (int i = 0; i < cellX; i++)
            {
                for(int j = 0; j < cellY; j++)
                {
                    int index = i * cellY + j;
                    CreateGridBlock(i, j, out gridCellVertexs[index], out gridCellColors[index], out gridCellMeshs[index]);
                    CreaeGridOutline(i, j, out gridOutlineVertexs[index], out gridOutlineMeshs[index]);
                }
            }

        }

        /// <summary>
        /// 创建一个网格块
        /// </summary>
        private void CreateGridBlock(int x,int y,out Vector3[] vertexs, out Color32[] colors, out Mesh mesh)
        {
            GameObject go = new GameObject();
            go.name = string.Concat("GridCell", x, "-", y);
            go.tag = "EditorOnly";//标记tag，不打入包中
            go.transform.parent = this.root;
            go.transform.localPosition = Vector3.zero;
            var renderer = go.AddComponent<MeshRenderer>();
            var collider = go.AddComponent<MeshCollider>();
            var filter = go.AddComponent<MeshFilter>();
            mesh = new Mesh();
            mesh.name = string.Concat("GridCell", x, "-", y);

            int startX = cellwidth * x; //起始块
            int startY = cellheight * y;
            int w = Mathf.Min(startX + cellwidth, this.row);
            int h = Mathf.Min(startY + cellheight, this.column);
            //剩余块的尺寸
            int curW = w - startX;
            int curH = h - startY;
            mesh.Clear();
            vertexs = new Vector3[curW * curH * 4];//每个格子占4个顶点
            int index = 0;
            for(int i = startX; i < w; i++)
            {
                for(int j = startY; j < h; j++)
                {
                    vertexs[index] = this.GetCellPositionLT(i, j);
                    vertexs[index+1] = this.GetCellPositionRT(i, j);
                    vertexs[index+2] = this.GetCellPositionRB(i, j);
                    vertexs[index+3] = this.GetCellPositionLB(i, j);
                    index += 4;
                }
            }
            mesh.vertices = vertexs;
            //设置三角形顶点下标，f(x)-2=总三角面数，一个三角面由3个顶点组成，则需要n个面
            //需要n*3大小的数组，才能保存所有三角面的顶点下标。
            //逆时针绘制是朝着屏幕可见，顺时针绘制是逆着屏幕可见
            //左上角为0，左下角为1， 右上角为3 。右下角为2
            //逆时针三角面渲染顶点顺序：012 230 
            //顺时针三角面渲染顶点顺序：021 320 = 210 032
            int[] tris = new int[(vertexs.Length - 2) * 3];
            for (int i = 0, j = 0; i < vertexs.Length; j += 6, i += 4)
            {
                tris[j + 0] = i;
                tris[j + 1] = i + 2;
                tris[j + 2] = i+1 ;

                tris[j + 3] = i+3 ;
                tris[j + 4] = i + 2;
                tris[j + 5] = i ;
            }
            mesh.triangles = tris;
            //每个格子占6个顶点下标，每格顶点数间隔4
            //int[] array = new int[6 * curW * curH];
            //for (int k = 0; k < curW * curH; k++)
            //{
            //    array[6 * k] = 4 * k + 2;
            //    array[6 * k + 1] = 4 * k + 1;
            //    array[6 * k + 2] = 4 * k;
            //    array[6 * k + 3] = 4 * k;
            //    array[6 * k + 4] = 4 * k + 3;
            //    array[6 * k + 5] = 4 * k + 2;
            //}

            //mesh.triangles = array;
            //顶点颜色
            colors = new Color32[curW * curH * 4];
            index = 0;
            for (int i = startX; i < w; i++)
            {
                for (int j = startY; j < h; j++)
                {
                    Color grey = Color.grey;
                    colors[index] = grey;
                    colors[index + 1] = grey;
                    colors[index + 2] = grey;
                    colors[index + 3] = grey;
                    index += 4;
                }
            }

            mesh.colors32 = colors;
            mesh.RecalculateBounds();
            collider.sharedMesh = mesh;
            filter.sharedMesh = mesh;
            renderer.material = new Material(Shader.Find("Nirvana/Editor/GridCell"));
        }

        /// <summary>
        /// 创建网格线
        /// </summary>
        private void CreaeGridOutline(int x, int y, out Vector3[] vertexs, out Mesh mesh)
        {
            GameObject go = new GameObject();
            go.name = string.Concat("GridOutline:", x, "-", y);
            go.tag = "EditorOnly";//标记tag，不打入包中
            go.transform.parent = this.root;
            go.transform.localPosition = Vector3.zero;
            var renderer = go.AddComponent<MeshRenderer>();
            var filter = go.AddComponent<MeshFilter>();
            mesh = new Mesh();
            mesh.name = string.Concat("GridOutline:", x, "-", y);

            int startX = cellwidth * x; //起始块
            int startY = cellheight * y;
            int w = Mathf.Min(startX + cellwidth, this.row);
            int h = Mathf.Min(startY + cellheight, this.column);
            //剩余块的尺寸
            int curW = w - startX;
            int curH = h - startY;
            mesh.Clear();
            vertexs = new Vector3[curW * curH * 4];//每个格子占4个顶点
            int index = 0;
            for (int i = startX; i < w; i++)
            {
                for (int j = startY; j < h; j++)
                {
                    vertexs[index] = this.GetCellPositionLT(i, j);
                    vertexs[index + 1] = this.GetCellPositionRT(i, j);
                    vertexs[index + 2] = this.GetCellPositionRB(i, j);
                    vertexs[index + 3] = this.GetCellPositionLB(i, j);
                    index += 4;
                }
            }
            mesh.vertices = vertexs;
            //设置子网格的索引缓冲区，以mesh绘制线段，则需要起点和终点。
            //因此一个cell，需要8个空间保存下标点。 
            //每个格子占8个顶点下标，每格顶点数间隔4
            int[] tris = new int[8 * curW * curH];
            for (int k = 0; k < curW * curH; k++)
            {
                tris[8 * k] = 4 * k;//左上角为0为起点
                tris[8 * k + 1] = 4 * k + 1;//左下角1为结束点
                tris[8 * k + 2] = 4 * k+1;//1为起始点
                tris[8 * k + 3] = 4 * k+2;
                tris[8 * k + 4] = 4 * k + 2;
                tris[8 * k + 5] = 4 * k + 3;
                tris[8 * k + 6] = 4 * k + 3;
                tris[8 * k + 7] = 4 * k ;
            }

            //SetTriangles和triangles总是使网格组成三角面
            // mesh.triangles = tris;
            //使用SetIndices你可以通过线或点来创建网格
            //MeshTopology.LineStrip:线带，绘制一段宽度的线
            //MeshTopology.Lines:线
            mesh.SetIndices(tris, MeshTopology.Lines, 0);
            mesh.RecalculateBounds();
            filter.sharedMesh = mesh;
            renderer.material = new Material(Shader.Find("Nirvana/Editor/GridOutline"));

        }


        /// <summary>
        /// 获取左顶点的坐标：俯视时，各顶点看起来在一2d平面上
        /// </summary>
        /// <param name="x"></param>
        /// <param name="y"></param>
        /// <returns></returns>
        public  Vector3 GetCellPositionLT(int x,int y)
        {
            float height;
            if(x >= 0 && x <this.row && y >=0 && y < this.column)
            {
                height = this.GetCell(x, y).HeightLT;
            }
            else
            {
                height = this.GetCell(0, 0).HeightLT;
            }

            return new Vector3(x * this.cellSize.x, height, y * this.cellSize.y);
        }

        /// <summary>
        /// 右顶点坐标
        /// </summary>
        public Vector3 GetCellPositionRT(int x, int y)
        {
            float height;
            if (x >= 0 && x < this.row && y >= 0 && y < this.column)
            {
                height = this.GetCell(x, y).HeightRT;
            }
            else
            {
                height = this.GetCell(0, 0).HeightRT;
            }

            return new Vector3((x+1) * this.cellSize.x, height, y * this.cellSize.y);
        }
        public Vector3 GetCellPositionLB(int x, int y)
        {
            float height;
            if (x >= 0 && x < this.row && y >= 0 && y < this.column)
            {
                height = this.GetCell(x, y).HeightLB;
            }
            else
            {
                height = this.GetCell(0, 0).HeightLB;
            }

            return new Vector3(x * this.cellSize.x, height, (y+1) * this.cellSize.y);
        }
        public Vector3 GetCellPositionRB(int x, int y)
        {
            float height;
            if (x >= 0 && x < this.row && y >= 0 && y < this.column)
            {
                height = this.GetCell(x, y).HeightRB;
            }
            else
            {
                height = this.GetCell(0, 0).HeightRB;
            }

            return new Vector3((x+1) * this.cellSize.x, height, (y + 1) * this.cellSize.y);
        }
        public AbstractSceneCell GetCell(int x,int y)
        {
            if (x >= 0 && x < this.row && y >= 0 && y < this.column)
            {
                return this.Cells[x * this.column + y];
            }
            return null;
        }


        public void FillGridColor(Func<AbstractSceneCell, Color> getCellColor)
        {
            if (this.gridCellMeshs == null || this.gridCellColors == null) this.UpdateMesh();
            //块数
            int cellX = Mathf.CeilToInt(this.row * 1.0f / cellwidth);
            int cellY = Mathf.CeilToInt(this.column * 1.0f / cellheight);

            for(int i = 0; i < cellX; i++)
            {
                for(int j = 0; j < cellY; j++)
                {
                    int index = i * cellY + j;
                    Color32[] colors = this.gridCellColors[index];
                    Mesh mesh = this.gridCellMeshs[index];
                    this.InitCellColor(i, j, colors, mesh, getCellColor);
                }
            }
        }

        public void FitToGround()
        {
            if (this.Cells == null) return;
            Vector3 pos = this.transform.position;
            for (int i = 0; i < this.row; i++)
            {
                for(int j = 0; j < this.column; j++)
                {
                    AbstractSceneCell cell = this.GetCell(i, j);
                    //i * this.cellSize.x + pos.x, j * this.cellSize.y + pos.z：先转世界坐标
                    //-pos.y :再放回到root节点下转回子节点坐标
                    cell.HeightLT = this.GetHeight(i * this.cellSize.x + pos.x, j * this.cellSize.y + pos.z) - pos.y;
                    cell.HeightRT = this.GetHeight((i + 1) * this.cellSize.x + pos.x, j * this.cellSize.y + pos.z) - pos.y;
                    cell.HeightLB = this.GetHeight(i * this.cellSize.x + pos.x, (j + 1) * this.cellSize.y + pos.z) - pos.y;
                    cell.HeightRB = this.GetHeight((i + 1) * this.cellSize.x + pos.x, (j + 1) * this.cellSize.y + pos.z) - pos.y;
                }
            }
            this.UpdateMesh();
        }



        private void InitCellColor(int blockX,int blockY,Color32[] colors,Mesh mesh, Func<AbstractSceneCell, Color> getCellColor)
        {
            int maxW = cellwidth * blockX;
            int maxH = cellheight * blockY;
            int curW = Mathf.Min(cellwidth + maxW, this.row);
            int curH = Mathf.Min(cellheight + maxH, this.column);
            int index = 0;
            for(int i = maxW; i < curW; i++)
            {
                for(int j = maxH; j < curH; j++)
                {
                    var cell = this.GetCell(i, j);
                    Color color = getCellColor(cell);
                    //顶点颜色
                    colors[index] = color;
                    colors[index+1] = color;
                    colors[index+2] = color;
                    colors[index+3] = color;
                    index += 4;
                }
            }
            mesh.colors32 = colors;
        }

    }

}

